package log

import (
	"bytes"
	"fmt"
	"log"
	"os"
)

const (
	NULL    = iota
	TRACE   = 1
	DEBUG   = 2
	INFO    = 3
	WARNING = 4
	ERROR   = 5
	FATAL   = 6
)

const DefaultLogPath = "cache.log"

// general log interface
type Log interface {
	Trace(v ...interface{})
	Debug(v ...interface{})
	Info(v ...interface{})
	Warning(v ...interface{})
	Error(v ...interface{})
	Fatal(v ...interface{})
	Tracef(format string, v ...interface{})
	Debugf(format string, v ...interface{})
	Infof(format string, v ...interface{})
	Warningf(format string, v ...interface{})
	Errorf(format string, v ...interface{})
	Fatalf(format string, v ...interface{})
}

type logger struct {
	*log.Logger
	options *Options
}

var DefaultLog *logger

type Level int

// NewFileLogger 打印日志到文件中
func NewFileLogger(path string, prefix string, flag int) *logger {
	logFile, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
	if err != nil {
		fmt.Println("open file, error : ", err)
	}
	log := &logger{
		Logger:  log.New(logFile, prefix, flag),
		options: &Options{},
	}
	return log
}

//NewConsoleLogger 打印日志到控制台
func NewConsoleLogger(prefix string, flag int) *logger {

	log := &logger{
		Logger:  log.New(os.Stdout, prefix, flag),
		options: &Options{},
	}
	return log
}

func init() {
	DefaultLog = NewConsoleLogger("", log.LstdFlags)

}

//String 将枚举类型的日志等级转化为字符串
func (level Level) String() string {
	switch level {
	case TRACE:
		return "trace"
	case DEBUG:
		return "debug"
	case INFO:
		return "info"
	case WARNING:
		return "warning"
	case ERROR:
		return "error"
	case FATAL:
		return "fatal"
	default:
		return "unkown"
	}

}

type Options struct {
	path  string `default:"../log/cache.log"` // 日志文件路径前缀，文件名为 gorpc.4019-09-46.log
	frame string `default:"../log/frame.log"` // 框架 panic 日志打印路径，默认 ../log/frame.log
	level Level  `default:"debug"`            // 日志级别，默认为 debug
}

//Option 利用装饰者模式，修改日志的默认设定
type Option func(*Options)

//WithPath 设置日志的路径
func WithPath(path string) Option {
	return func(o *Options) {
		o.path = path
	}
}

//WithFrame 设置框架 panic 日志打印路径
func WithFrame(frame string) Option {
	return func(o *Options) {
		o.frame = frame
	}
}

//WithLevel 设置日志级别
func WithLevel(level Level) Option {
	return func(o *Options) {
		o.level = level
	}
}

// Trace print trace log
func Trace(v ...interface{}) {
	DefaultLog.Trace(v...)
}

// Tracef print a formatted trace log
func Tracef(format string, v ...interface{}) {
	DefaultLog.Tracef(format, v...)
}

func (log *logger) Trace(v ...interface{}) {
	if log.options.level > TRACE {
		return
	}
	data := log.Prefix() + fmt.Sprint(v...)
	Output(log, 4, "[TRACE] ", data)
}

func (log *logger) Tracef(format string, v ...interface{}) {
	if log.options.level > TRACE {
		return
	}
	data := log.Prefix() + fmt.Sprintf(format, v...)
	Output(log, 4, "[TRACE] ", data)
}

// Debug print debug log
func Debug(v ...interface{}) {
	DefaultLog.Debug(v...)
}

// Debugf print a formatted debug log
func Debugf(format string, v ...interface{}) {
	DefaultLog.Debugf(format, v...)
}

func (log *logger) Debug(v ...interface{}) {
	if log.options.level > DEBUG {
		return
	}
	data := log.Prefix() + fmt.Sprint(v...)
	Output(log, 4, "[DEBUG] ", data)
}

func (log *logger) Debugf(format string, v ...interface{}) {
	if log.options.level > DEBUG {
		return
	}
	data := log.Prefix() + fmt.Sprintf(format, v...)
	Output(log, 4, "[DEBUG] ", data)
}

// Info print info log
func Info(v ...interface{}) {
	DefaultLog.Info(v...)
}

// Infof print a formatted info log
func Infof(format string, v ...interface{}) {
	DefaultLog.Infof(format, v...)
}

func (log *logger) Info(v ...interface{}) {
	if log.options.level > INFO {
		return
	}
	data := log.Prefix() + fmt.Sprint(v...)
	Output(log, 4, "[INFO] ", data)
}

func (log *logger) Infof(format string, v ...interface{}) {
	if log.options.level > INFO {
		return
	}
	data := log.Prefix() + fmt.Sprintf(format, v...)
	Output(log, 4, "[INFO] ", data)
}

// Warning print warning log
func Warning(v ...interface{}) {
	DefaultLog.Warning(v...)
}

// Warningf print a formatted warning log
func Warningf(format string, v ...interface{}) {
	DefaultLog.Warningf(format, v...)
}

func (log *logger) Warning(v ...interface{}) {
	if log.options.level > WARNING {
		return
	}
	data := log.Prefix() + fmt.Sprint(v...)
	Output(log, 4, "[WARNING] ", data)
}

func (log *logger) Warningf(format string, v ...interface{}) {
	if log.options.level > WARNING {
		return
	}
	data := log.Prefix() + fmt.Sprintf(format, v...)
	Output(log, 4, "[WARNING] ", data)
}

// Error print error log
func Error(v ...interface{}) {
	DefaultLog.Error(v...)
}

// Errorf print a formatted error log
func Errorf(format string, v ...interface{}) {
	DefaultLog.Errorf(format, v...)
}

func (log *logger) Error(v ...interface{}) {
	if log.options.level > ERROR {
		return
	}
	data := log.Prefix() + fmt.Sprint(v...)
	Output(log, 4, "[ERROR] ", data)
}

func (log *logger) Errorf(format string, v ...interface{}) {
	if log.options.level > ERROR {
		return
	}
	data := log.Prefix() + fmt.Sprintf(format, v...)
	Output(log, 4, "[ERROR] ", data)
}

// Fatal print fatal log
func Fatal(v ...interface{}) {
	DefaultLog.Fatal(v...)
}

// Fatalf print a formatted fatal log
func Fatalf(format string, v ...interface{}) {
	DefaultLog.Fatalf(format, v...)
}

func (log *logger) Fatal(v ...interface{}) {
	if log.options.level > FATAL {
		return
	}
	data := log.Prefix() + fmt.Sprint(v...)
	Output(log, 4, "[FATAL] ", data)
}

func (log *logger) Fatalf(format string, v ...interface{}) {
	if log.options.level > FATAL {
		return
	}
	data := log.Prefix() + fmt.Sprintf(format, v...)
	Output(log, 4, "[FATAL] ", data)
}

// Output called to write log
func Output(log *logger, calldepth int, prefix string, data string) {
	var buffer bytes.Buffer
	buffer.WriteString(prefix)
	buffer.WriteString(data)
	log.Output(calldepth, buffer.String())
}
